
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>


#include "http_error.h"
#include "http_factory.h"
#include "http_conn.h"

CHTTPConnFactory::CHTTPConnFactory(CHTTPLogger& refLogger, HTTP_CONF_S& refConf, CHTTPEventPoller& refPoller):
    m_refLogger(refLogger),
    m_refConf(refConf),
    m_refPoller(refPoller)
{

}

CHTTPConnFactory::~CHTTPConnFactory()
{
    
}


IHTTPEvent* CHTTPConnFactory::CreateListener()
{
    int iEnable;
    int iErr;
    int iFd;
    struct sockaddr_in stLocalAddr;

    iFd = socket(AF_INET, SOCK_STREAM, 0);
    if (iFd == -1)
    {
        HTTP_ERROR(HTTP_SYSCALL_FAIL, "socket error, %s", strerror(errno));
        return NULL;
    }

    // 用于快速重启服务，当一个TCP套接字处于TIME_WAIT状态时，新启动的程序可以使用该选项来占用该地址和端口。
    // 通常，一个端口在释放后会等待两分钟才能再次被使用，但SO_REUSEADDR允许端口释放后立即被再次使用。
    iEnable = 1;
	iErr = setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, &iEnable, sizeof(int));
    if (iErr == -1)
    {
        HTTP_ERROR(HTTP_SYSCALL_FAIL, "setsockopt error, %s", strerror(errno));
        close(iFd);
        return NULL;
    }

    // SO_REUSEPORT
    // 允许多个socket绑定在相同的地址上(identical socket address)，常用于负载分担

    stLocalAddr.sin_family = AF_INET;
    stLocalAddr.sin_addr = m_refConf.stAddr.sin_addr;
    stLocalAddr.sin_port = htons(m_refConf.stAddr.sin_port);
    iErr = bind(iFd, (const struct sockaddr*)&stLocalAddr, sizeof(stLocalAddr));
    if (iErr == -1)
    {
        HTTP_ERROR(HTTP_SYSCALL_FAIL, "bind error, %s", strerror(errno));
        close(iFd);
        return NULL;
    }

    iErr = listen(iFd, 1024);
    if (iErr == -1)
    {
        HTTP_ERROR(HTTP_SYSCALL_FAIL, "listen error, %s", strerror(errno));
        close(iFd);
        return NULL;
    }

    CHTTPConnection* pobjConn = new (std::nothrow) CHTTPConnection(m_refLogger, *this, m_refPoller, iFd, true);
    if (pobjConn == NULL)
    {
        HTTP_ERROR(HTTP_ALLOC_MEMORY_FAIL, "Create connection error");
        close(iFd);
        return NULL;
    }

    m_vecEvents.push_back(pobjConn);

    char szAddr[INET6_ADDRSTRLEN];
    inet_ntop(AF_INET, &m_refConf.stAddr.sin_addr, szAddr, INET6_ADDRSTRLEN);
    HTTP_INFO("Listened on %s:%hu successfully, sockfd=%d", szAddr, m_refConf.stAddr.sin_port, iFd);

    return pobjConn;
}

IHTTPEvent* CHTTPConnFactory::CreateAcceptor(IHTTPEvent& refListener)
{
    struct sockaddr_in stPeerAddr;
    socklen_t ulPeerLen = sizeof(stPeerAddr);
    int iListenerFd = refListener.GetEventFd();

    if (iListenerFd == -1)
    {
        HTTP_ERROR(HTTP_INVALID_PARAM, "Invalid listener");
        return nullptr;
    }

    int iAcceptFd = accept(iListenerFd, (struct sockaddr*)&stPeerAddr, &ulPeerLen);
    if (iAcceptFd == -1)
    {
        HTTP_ERROR(HTTP_SYSCALL_FAIL, "accept error, %s", strerror(errno));
        return nullptr;
    }

    CHTTPConnection* pobjConn = new (std::nothrow) CHTTPConnection(m_refLogger, *this, m_refPoller, iAcceptFd, false);
    if (pobjConn == NULL)
    {
        HTTP_ERROR(HTTP_ALLOC_MEMORY_FAIL, "Create connection error");
        close(iAcceptFd);
        return NULL;
    }

    CHTTPParser* pobjParser = new (std::nothrow) CHTTPParser();
    if (pobjParser == nullptr)
    {
        close(iAcceptFd);
        delete pobjConn;
        return nullptr;
    }

    pobjConn->SetParser(*pobjParser);

    char szAddr[INET6_ADDRSTRLEN];
    inet_ntop(AF_INET, &stPeerAddr.sin_addr, szAddr, INET6_ADDRSTRLEN);
    HTTP_INFO("Accepted new client %s:%hu successfully, sockfd=%d", szAddr, htons(stPeerAddr.sin_port), iAcceptFd);
    
    m_vecEvents.push_back(pobjConn);

    return pobjConn;
}